﻿// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors
// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE

#if !defined(JSON_IS_AMALGAMATION)
#include "json_tool.h"
#include "../assertions.h"
#include "../reader.h"
#include "../value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iostream>
#include <istream>
#include <limits>
#include <memory>
#include <set>
#include <sstream>
#include <utility>

#include <cstdio>
#if __cplusplus >= 201103L

#if !defined(sscanf)
#define sscanf std::sscanf
#endif

#endif //__cplusplus

#if defined(_MSC_VER)
#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
#endif //_MSC_VER

#if defined(_MSC_VER)
// Disable warning about strdup being deprecated.
#pragma warning(disable : 4996)
#endif

// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
// time to change the stack limit
#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
#define JSONCPP_DEPRECATED_STACK_LIMIT 1000
#endif

static size_t const stackLimit_g =
JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()

namespace Json {
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
	using CharReaderPtr = std::unique_ptr<CharReader>;
#else
	using CharReaderPtr = std::auto_ptr<CharReader>;
#endif

	// Implementation of class Features
	// ////////////////////////////////

	Features::Features() = default;

	Features Features::all() { return {}; }

	Features Features::strictMode() {
		Features features;
		features.allowComments_ = false;
		features.strictRoot_ = true;
		features.allowDroppedNullPlaceholders_ = false;
		features.allowNumericKeys_ = false;
		return features;
	}

	// Implementation of class Reader
	// ////////////////////////////////

	bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
		return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
	}

	// Class Reader
	// //////////////////////////////////////////////////////////////////

	Reader::Reader() : features_(Features::all()) {}

	Reader::Reader(const Features& features) : features_(features) {}

	bool Reader::parse(const std::string& document, Value& root,
		bool collectComments) {
		document_.assign(document.begin(), document.end());
		const char* begin = document_.c_str();
		const char* end = begin + document_.length();
		return parse(begin, end, root, collectComments);
	}

	bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
		// std::istream_iterator<char> begin(is);
		// std::istream_iterator<char> end;
		// Those would allow streamed input from a file, if parse() were a
		// template function.

		// Since String is reference-counted, this at least does not
		// create an extra copy.
		String doc(std::istreambuf_iterator<char>(is), {});
		return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
	}

	bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
		bool collectComments) {
		if (!features_.allowComments_) {
			collectComments = false;
		}

		begin_ = beginDoc;
		end_ = endDoc;
		collectComments_ = collectComments;
		current_ = begin_;
		lastValueEnd_ = nullptr;
		lastValue_ = nullptr;
		commentsBefore_.clear();
		errors_.clear();
		while (!nodes_.empty())
			nodes_.pop();
		nodes_.push(&root);

		bool successful = readValue();
		Token token;
		skipCommentTokens(token);
		if (collectComments_ && !commentsBefore_.empty())
			root.setComment(commentsBefore_, commentAfter);
		if (features_.strictRoot_) {
			if (!root.isArray() && !root.isObject()) {
				// Set error location to start of doc, ideally should be first token found
				// in doc
				token.type_ = tokenError;
				token.start_ = beginDoc;
				token.end_ = endDoc;
				addError(
					"A valid JSON document must be either an array or an object value.",
					token);
				return false;
			}
		}
		return successful;
	}

	bool Reader::readValue() {
		// readValue() may call itself only if it calls readObject() or ReadArray().
		// These methods execute nodes_.push() just before and nodes_.pop)() just
		// after calling readValue(). parse() executes one nodes_.push(), so > instead
		// of >=.
		if (nodes_.size() > stackLimit_g)
			throwRuntimeError("Exceeded stackLimit in readValue().");

		Token token;
		skipCommentTokens(token);
		bool successful = true;

		if (collectComments_ && !commentsBefore_.empty()) {
			currentValue().setComment(commentsBefore_, commentBefore);
			commentsBefore_.clear();
		}

		switch (token.type_) {
		case tokenObjectBegin:
			successful = readObject(token);
			currentValue().setOffsetLimit(current_ - begin_);
			break;
		case tokenArrayBegin:
			successful = readArray(token);
			currentValue().setOffsetLimit(current_ - begin_);
			break;
		case tokenNumber:
			successful = decodeNumber(token);
			break;
		case tokenString:
			successful = decodeString(token);
			break;
		case tokenTrue: {
			Value v(true);
			currentValue().swapPayload(v);
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
		} break;
		case tokenFalse: {
			Value v(false);
			currentValue().swapPayload(v);
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
		} break;
		case tokenNull: {
			Value v;
			currentValue().swapPayload(v);
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
		} break;
		case tokenArraySeparator:
		case tokenObjectEnd:
		case tokenArrayEnd:
			if (features_.allowDroppedNullPlaceholders_) {
				// "Un-read" the current token and mark the current value as a null
				// token.
				current_--;
				Value v;
				currentValue().swapPayload(v);
				currentValue().setOffsetStart(current_ - begin_ - 1);
				currentValue().setOffsetLimit(current_ - begin_);
				break;
			} // Else, fall through...
		default:
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
			return addError("Syntax error: value, object or array expected.", token);
		}

		if (collectComments_) {
			lastValueEnd_ = current_;
			lastValue_ = &currentValue();
		}

		return successful;
	}

	void Reader::skipCommentTokens(Token& token) {
		if (features_.allowComments_) {
			do {
				readToken(token);
			} while (token.type_ == tokenComment);
		}
		else {
			readToken(token);
		}
	}

	bool Reader::readToken(Token& token) {
		skipSpaces();
		token.start_ = current_;
		Char c = getNextChar();
		bool ok = true;
		switch (c) {
		case '{':
			token.type_ = tokenObjectBegin;
			break;
		case '}':
			token.type_ = tokenObjectEnd;
			break;
		case '[':
			token.type_ = tokenArrayBegin;
			break;
		case ']':
			token.type_ = tokenArrayEnd;
			break;
		case '"':
			token.type_ = tokenString;
			ok = readString();
			break;
		case '/':
			token.type_ = tokenComment;
			ok = readComment();
			break;
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case '-':
			token.type_ = tokenNumber;
			readNumber();
			break;
		case 't':
			token.type_ = tokenTrue;
			ok = match("rue", 3);
			break;
		case 'f':
			token.type_ = tokenFalse;
			ok = match("alse", 4);
			break;
		case 'n':
			token.type_ = tokenNull;
			ok = match("ull", 3);
			break;
		case ',':
			token.type_ = tokenArraySeparator;
			break;
		case ':':
			token.type_ = tokenMemberSeparator;
			break;
		case 0:
			token.type_ = tokenEndOfStream;
			break;
		default:
			ok = false;
			break;
		}
		if (!ok)
			token.type_ = tokenError;
		token.end_ = current_;
		return ok;
	}

	void Reader::skipSpaces() {
		while (current_ != end_) {
			Char c = *current_;
			if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
				++current_;
			else
				break;
		}
	}

	bool Reader::match(const Char* pattern, int patternLength) {
		if (end_ - current_ < patternLength)
			return false;
		int index = patternLength;
		while (index--)
			if (current_[index] != pattern[index])
				return false;
		current_ += patternLength;
		return true;
	}

	bool Reader::readComment() {
		Location commentBegin = current_ - 1;
		Char c = getNextChar();
		bool successful = false;
		if (c == '*')
			successful = readCStyleComment();
		else if (c == '/')
			successful = readCppStyleComment();
		if (!successful)
			return false;

		if (collectComments_) {
			CommentPlacement placement = commentBefore;
			if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
				if (c != '*' || !containsNewLine(commentBegin, current_))
					placement = commentAfterOnSameLine;
			}

			addComment(commentBegin, current_, placement);
		}
		return true;
	}

	String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
		String normalized;
		normalized.reserve(static_cast<size_t>(end - begin));
		Reader::Location current = begin;
		while (current != end) {
			char c = *current++;
			if (c == '\r') {
				if (current != end && *current == '\n')
					// convert dos EOL
					++current;
				// convert Mac EOL
				normalized += '\n';
			}
			else {
				normalized += c;
			}
		}
		return normalized;
	}

	void Reader::addComment(Location begin, Location end,
		CommentPlacement placement) {
		assert(collectComments_);
		const String& normalized = normalizeEOL(begin, end);
		if (placement == commentAfterOnSameLine) {
			assert(lastValue_ != nullptr);
			lastValue_->setComment(normalized, placement);
		}
		else {
			commentsBefore_ += normalized;
		}
	}

	bool Reader::readCStyleComment() {
		while ((current_ + 1) < end_) {
			Char c = getNextChar();
			if (c == '*' && *current_ == '/')
				break;
		}
		return getNextChar() == '/';
	}

	bool Reader::readCppStyleComment() {
		while (current_ != end_) {
			Char c = getNextChar();
			if (c == '\n')
				break;
			if (c == '\r') {
				// Consume DOS EOL. It will be normalized in addComment.
				if (current_ != end_ && *current_ == '\n')
					getNextChar();
				// Break on Moc OS 9 EOL.
				break;
			}
		}
		return true;
	}

	void Reader::readNumber() {
		Location p = current_;
		char c = '0'; // stopgap for already consumed character
		// integral part
		while (c >= '0' && c <= '9')
			c = (current_ = p) < end_ ? *p++ : '\0';
		// fractional part
		if (c == '.') {
			c = (current_ = p) < end_ ? *p++ : '\0';
			while (c >= '0' && c <= '9')
				c = (current_ = p) < end_ ? *p++ : '\0';
		}
		// exponential part
		if (c == 'e' || c == 'E') {
			c = (current_ = p) < end_ ? *p++ : '\0';
			if (c == '+' || c == '-')
				c = (current_ = p) < end_ ? *p++ : '\0';
			while (c >= '0' && c <= '9')
				c = (current_ = p) < end_ ? *p++ : '\0';
		}
	}

	bool Reader::readString() {
		Char c = '\0';
		while (current_ != end_) {
			c = getNextChar();
			if (c == '\\')
				getNextChar();
			else if (c == '"')
				break;
		}
		return c == '"';
	}

	bool Reader::readObject(Token& token) {
		Token tokenName;
		String name;
		Value init(objectValue);
		currentValue().swapPayload(init);
		currentValue().setOffsetStart(token.start_ - begin_);
		while (readToken(tokenName)) {
			bool initialTokenOk = true;
			while (tokenName.type_ == tokenComment && initialTokenOk)
				initialTokenOk = readToken(tokenName);
			if (!initialTokenOk)
				break;
			if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
				return true;
			name.clear();
			if (tokenName.type_ == tokenString) {
				if (!decodeString(tokenName, name))
					return recoverFromError(tokenObjectEnd);
			}
			else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
				Value numberName;
				if (!decodeNumber(tokenName, numberName))
					return recoverFromError(tokenObjectEnd);
				name = numberName.asString();
			}
			else {
				break;
			}

			Token colon;
			if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
				return addErrorAndRecover("Missing ':' after object member name", colon,
					tokenObjectEnd);
			}
			Value& value = currentValue()[name];
			nodes_.push(&value);
			bool ok = readValue();
			nodes_.pop();
			if (!ok) // error already set
				return recoverFromError(tokenObjectEnd);

			Token comma;
			if (!readToken(comma) ||
				(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
					comma.type_ != tokenComment)) {
				return addErrorAndRecover("Missing ',' or '}' in object declaration",
					comma, tokenObjectEnd);
			}
			bool finalizeTokenOk = true;
			while (comma.type_ == tokenComment && finalizeTokenOk)
				finalizeTokenOk = readToken(comma);
			if (comma.type_ == tokenObjectEnd)
				return true;
		}
		return addErrorAndRecover("Missing '}' or object member name", tokenName,
			tokenObjectEnd);
	}

	bool Reader::readArray(Token& token) {
		Value init(arrayValue);
		currentValue().swapPayload(init);
		currentValue().setOffsetStart(token.start_ - begin_);
		skipSpaces();
		if (current_ != end_ && *current_ == ']') // empty array
		{
			Token endArray;
			readToken(endArray);
			return true;
		}
		int index = 0;
		for (;;) {
			Value& value = currentValue()[index++];
			nodes_.push(&value);
			bool ok = readValue();
			nodes_.pop();
			if (!ok) // error already set
				return recoverFromError(tokenArrayEnd);

			Token currentToken;
			// Accept Comment after last item in the array.
			ok = readToken(currentToken);
			while (currentToken.type_ == tokenComment && ok) {
				ok = readToken(currentToken);
			}
			bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
				currentToken.type_ != tokenArrayEnd);
			if (!ok || badTokenType) {
				return addErrorAndRecover("Missing ',' or ']' in array declaration",
					currentToken, tokenArrayEnd);
			}
			if (currentToken.type_ == tokenArrayEnd)
				break;
		}
		return true;
	}

	bool Reader::decodeNumber(Token& token) {
		Value decoded;
		if (!decodeNumber(token, decoded))
			return false;
		currentValue().swapPayload(decoded);
		currentValue().setOffsetStart(token.start_ - begin_);
		currentValue().setOffsetLimit(token.end_ - begin_);
		return true;
	}

	bool Reader::decodeNumber(Token& token, Value& decoded) {
		// Attempts to parse the number as an integer. If the number is
		// larger than the maximum supported value of an integer then
		// we decode the number as a double.
		Location current = token.start_;
		bool isNegative = *current == '-';
		if (isNegative)
			++current;
		// TODO: Help the compiler do the div and mod at compile time or get rid of
		// them.
		Value::LargestUInt maxIntegerValue =
			isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
			: Value::maxLargestUInt;
		Value::LargestUInt threshold = maxIntegerValue / 10;
		Value::LargestUInt value = 0;
		while (current < token.end_) {
			Char c = *current++;
			if (c < '0' || c > '9')
				return decodeDouble(token, decoded);
			auto digit(static_cast<Value::UInt>(c - '0'));
			if (value >= threshold) {
				// We've hit or exceeded the max value divided by 10 (rounded down). If
				// a) we've only just touched the limit, b) this is the last digit, and
				// c) it's small enough to fit in that rounding delta, we're okay.
				// Otherwise treat this number as a double to avoid overflow.
				if (value > threshold || current != token.end_ ||
					digit > maxIntegerValue % 10) {
					return decodeDouble(token, decoded);
				}
			}
			value = value * 10 + digit;
		}
		if (isNegative && value == maxIntegerValue)
			decoded = Value::minLargestInt;
		else if (isNegative)
			decoded = -Value::LargestInt(value);
		else if (value <= Value::LargestUInt(Value::maxInt))
			decoded = Value::LargestInt(value);
		else
			decoded = value;
		return true;
	}

	bool Reader::decodeDouble(Token& token) {
		Value decoded;
		if (!decodeDouble(token, decoded))
			return false;
		currentValue().swapPayload(decoded);
		currentValue().setOffsetStart(token.start_ - begin_);
		currentValue().setOffsetLimit(token.end_ - begin_);
		return true;
	}

	bool Reader::decodeDouble(Token& token, Value& decoded) {
		double value = 0;
		String buffer(token.start_, token.end_);
		IStringStream is(buffer);
		if (!(is >> value))
			return addError(
				"'" + String(token.start_, token.end_) + "' is not a number.", token);
		decoded = value;
		return true;
	}

	bool Reader::decodeString(Token& token) {
		String decoded_string;
		if (!decodeString(token, decoded_string))
			return false;
		Value decoded(decoded_string);
		currentValue().swapPayload(decoded);
		currentValue().setOffsetStart(token.start_ - begin_);
		currentValue().setOffsetLimit(token.end_ - begin_);
		return true;
	}

	bool Reader::decodeString(Token& token, String& decoded) {
		decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
		Location current = token.start_ + 1; // skip '"'
		Location end = token.end_ - 1;       // do not include '"'
		while (current != end) {
			Char c = *current++;
			if (c == '"')
				break;
			if (c == '\\') {
				if (current == end)
					return addError("Empty escape sequence in string", token, current);
				Char escape = *current++;
				switch (escape) {
				case '"':
					decoded += '"';
					break;
				case '/':
					decoded += '/';
					break;
				case '\\':
					decoded += '\\';
					break;
				case 'b':
					decoded += '\b';
					break;
				case 'f':
					decoded += '\f';
					break;
				case 'n':
					decoded += '\n';
					break;
				case 'r':
					decoded += '\r';
					break;
				case 't':
					decoded += '\t';
					break;
				case 'u': {
					unsigned int unicode;
					if (!decodeUnicodeCodePoint(token, current, end, unicode))
						return false;
					decoded += codePointToUTF8(unicode);
				} break;
				default:
					return addError("Bad escape sequence in string", token, current);
				}
			}
			else {
				decoded += c;
			}
		}
		return true;
	}

	bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
		Location end, unsigned int& unicode) {
		if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
			return false;
		if (unicode >= 0xD800 && unicode <= 0xDBFF) {
			// surrogate pairs
			if (end - current < 6)
				return addError(
					"additional six characters expected to parse unicode surrogate pair.",
					token, current);
			if (*(current++) == '\\' && *(current++) == 'u') {
				unsigned int surrogatePair;
				if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
					unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
				}
				else
					return false;
			}
			else
				return addError("expecting another \\u token to begin the second half of "
					"a unicode surrogate pair",
					token, current);
		}
		return true;
	}

	bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
		Location end,
		unsigned int& ret_unicode) {
		if (end - current < 4)
			return addError(
				"Bad unicode escape sequence in string: four digits expected.", token,
				current);
		int unicode = 0;
		for (int index = 0; index < 4; ++index) {
			Char c = *current++;
			unicode *= 16;
			if (c >= '0' && c <= '9')
				unicode += c - '0';
			else if (c >= 'a' && c <= 'f')
				unicode += c - 'a' + 10;
			else if (c >= 'A' && c <= 'F')
				unicode += c - 'A' + 10;
			else
				return addError(
					"Bad unicode escape sequence in string: hexadecimal digit expected.",
					token, current);
		}
		ret_unicode = static_cast<unsigned int>(unicode);
		return true;
	}

	bool Reader::addError(const String& message, Token& token, Location extra) {
		ErrorInfo info;
		info.token_ = token;
		info.message_ = message;
		info.extra_ = extra;
		errors_.push_back(info);
		return false;
	}

	bool Reader::recoverFromError(TokenType skipUntilToken) {
		size_t const errorCount = errors_.size();
		Token skip;
		for (;;) {
			if (!readToken(skip))
				errors_.resize(errorCount); // discard errors caused by recovery
			if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
				break;
		}
		errors_.resize(errorCount);
		return false;
	}

	bool Reader::addErrorAndRecover(const String& message, Token& token,
		TokenType skipUntilToken) {
		addError(message, token);
		return recoverFromError(skipUntilToken);
	}

	Value& Reader::currentValue() { return *(nodes_.top()); }

	Reader::Char Reader::getNextChar() {
		if (current_ == end_)
			return 0;
		return *current_++;
	}

	void Reader::getLocationLineAndColumn(Location location, int& line,
		int& column) const {
		Location current = begin_;
		Location lastLineStart = current;
		line = 0;
		while (current < location && current != end_) {
			Char c = *current++;
			if (c == '\r') {
				if (*current == '\n')
					++current;
				lastLineStart = current;
				++line;
			}
			else if (c == '\n') {
				lastLineStart = current;
				++line;
			}
		}
		// column & line start at 1
		column = int(location - lastLineStart) + 1;
		++line;
	}

	String Reader::getLocationLineAndColumn(Location location) const {
		int line, column;
		getLocationLineAndColumn(location, line, column);
		char buffer[18 + 16 + 16 + 1];
		jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
		return buffer;
	}

	// Deprecated. Preserved for backward compatibility
	String Reader::getFormatedErrorMessages() const {
		return getFormattedErrorMessages();
	}

	String Reader::getFormattedErrorMessages() const {
		String formattedMessage;
		for (const auto& error : errors_) {
			formattedMessage +=
				"* " + getLocationLineAndColumn(error.token_.start_) + "\n";
			formattedMessage += "  " + error.message_ + "\n";
			if (error.extra_)
				formattedMessage +=
				"See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
		}
		return formattedMessage;
	}

	std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
		std::vector<Reader::StructuredError> allErrors;
		for (const auto& error : errors_) {
			Reader::StructuredError structured;
			structured.offset_start = error.token_.start_ - begin_;
			structured.offset_limit = error.token_.end_ - begin_;
			structured.message = error.message_;
			allErrors.push_back(structured);
		}
		return allErrors;
	}

	bool Reader::pushError(const Value& value, const String& message) {
		ptrdiff_t const length = end_ - begin_;
		if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
			return false;
		Token token;
		token.type_ = tokenError;
		token.start_ = begin_ + value.getOffsetStart();
		token.end_ = begin_ + value.getOffsetLimit();
		ErrorInfo info;
		info.token_ = token;
		info.message_ = message;
		info.extra_ = nullptr;
		errors_.push_back(info);
		return true;
	}

	bool Reader::pushError(const Value& value, const String& message,
		const Value& extra) {
		ptrdiff_t const length = end_ - begin_;
		if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
			extra.getOffsetLimit() > length)
			return false;
		Token token;
		token.type_ = tokenError;
		token.start_ = begin_ + value.getOffsetStart();
		token.end_ = begin_ + value.getOffsetLimit();
		ErrorInfo info;
		info.token_ = token;
		info.message_ = message;
		info.extra_ = begin_ + extra.getOffsetStart();
		errors_.push_back(info);
		return true;
	}

	bool Reader::good() const { return errors_.empty(); }

	// Originally copied from the Features class (now deprecated), used internally
	// for features implementation.
	class OurFeatures {
	public:
		static OurFeatures all();
		bool allowComments_;
		bool allowTrailingCommas_;
		bool strictRoot_;
		bool allowDroppedNullPlaceholders_;
		bool allowNumericKeys_;
		bool allowSingleQuotes_;
		bool failIfExtra_;
		bool rejectDupKeys_;
		bool allowSpecialFloats_;
		bool skipBom_;
		size_t stackLimit_;
	}; // OurFeatures

	OurFeatures OurFeatures::all() { return {}; }

	// Implementation of class Reader
	// ////////////////////////////////

	// Originally copied from the Reader class (now deprecated), used internally
	// for implementing JSON reading.
	class OurReader {
	public:
		using Char = char;
		using Location = const Char*;
		struct StructuredError {
			ptrdiff_t offset_start;
			ptrdiff_t offset_limit;
			String message;
		};

		explicit OurReader(OurFeatures const& features);
		bool parse(const char* beginDoc, const char* endDoc, Value& root,
			bool collectComments = true);
		String getFormattedErrorMessages() const;
		std::vector<StructuredError> getStructuredErrors() const;

	private:
		OurReader(OurReader const&);      // no impl
		void operator=(OurReader const&); // no impl

		enum TokenType {
			tokenEndOfStream = 0,
			tokenObjectBegin,
			tokenObjectEnd,
			tokenArrayBegin,
			tokenArrayEnd,
			tokenString,
			tokenNumber,
			tokenTrue,
			tokenFalse,
			tokenNull,
			tokenNaN,
			tokenPosInf,
			tokenNegInf,
			tokenArraySeparator,
			tokenMemberSeparator,
			tokenComment,
			tokenError
		};

		class Token {
		public:
			TokenType type_;
			Location start_;
			Location end_;
		};

		class ErrorInfo {
		public:
			Token token_;
			String message_;
			Location extra_;
		};

		using Errors = std::deque<ErrorInfo>;

		bool readToken(Token& token);
		void skipSpaces();
		void skipBom(bool skipBom);
		bool match(const Char* pattern, int patternLength);
		bool readComment();
		bool readCStyleComment(bool* containsNewLineResult);
		bool readCppStyleComment();
		bool readString();
		bool readStringSingleQuote();
		bool readNumber(bool checkInf);
		bool readValue();
		bool readObject(Token& token);
		bool readArray(Token& token);
		bool decodeNumber(Token& token);
		bool decodeNumber(Token& token, Value& decoded);
		bool decodeString(Token& token);
		bool decodeString(Token& token, String& decoded);
		bool decodeDouble(Token& token);
		bool decodeDouble(Token& token, Value& decoded);
		bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
			unsigned int& unicode);
		bool decodeUnicodeEscapeSequence(Token& token, Location& current,
			Location end, unsigned int& unicode);
		bool addError(const String& message, Token& token, Location extra = nullptr);
		bool recoverFromError(TokenType skipUntilToken);
		bool addErrorAndRecover(const String& message, Token& token,
			TokenType skipUntilToken);
		void skipUntilSpace();
		Value& currentValue();
		Char getNextChar();
		void getLocationLineAndColumn(Location location, int& line,
			int& column) const;
		String getLocationLineAndColumn(Location location) const;
		void addComment(Location begin, Location end, CommentPlacement placement);
		void skipCommentTokens(Token& token);

		static String normalizeEOL(Location begin, Location end);
		static bool containsNewLine(Location begin, Location end);

		using Nodes = std::stack<Value*>;

		Nodes nodes_{};
		Errors errors_{};
		String document_{};
		Location begin_ = nullptr;
		Location end_ = nullptr;
		Location current_ = nullptr;
		Location lastValueEnd_ = nullptr;
		Value* lastValue_ = nullptr;
		bool lastValueHasAComment_ = false;
		String commentsBefore_{};

		OurFeatures const features_;
		bool collectComments_ = false;
	}; // OurReader

	// complete copy of Read impl, for OurReader

	bool OurReader::containsNewLine(OurReader::Location begin,
		OurReader::Location end) {
		return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
	}

	OurReader::OurReader(OurFeatures const& features) : features_(features) {}

	bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
		bool collectComments) {
		if (!features_.allowComments_) {
			collectComments = false;
		}

		begin_ = beginDoc;
		end_ = endDoc;
		collectComments_ = collectComments;
		current_ = begin_;
		lastValueEnd_ = nullptr;
		lastValue_ = nullptr;
		commentsBefore_.clear();
		errors_.clear();
		while (!nodes_.empty())
			nodes_.pop();
		nodes_.push(&root);

		// skip byte order mark if it exists at the beginning of the UTF-8 text.
		skipBom(features_.skipBom_);
		bool successful = readValue();
		nodes_.pop();
		Token token;
		skipCommentTokens(token);
		if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
			addError("Extra non-whitespace after JSON value.", token);
			return false;
		}
		if (collectComments_ && !commentsBefore_.empty())
			root.setComment(commentsBefore_, commentAfter);
		if (features_.strictRoot_) {
			if (!root.isArray() && !root.isObject()) {
				// Set error location to start of doc, ideally should be first token found
				// in doc
				token.type_ = tokenError;
				token.start_ = beginDoc;
				token.end_ = endDoc;
				addError(
					"A valid JSON document must be either an array or an object value.",
					token);
				return false;
			}
		}
		return successful;
	}

	bool OurReader::readValue() {
		//  To preserve the old behaviour we cast size_t to int.
		if (nodes_.size() > features_.stackLimit_)
			throwRuntimeError("Exceeded stackLimit in readValue().");
		Token token;
		skipCommentTokens(token);
		bool successful = true;

		if (collectComments_ && !commentsBefore_.empty()) {
			currentValue().setComment(commentsBefore_, commentBefore);
			commentsBefore_.clear();
		}

		switch (token.type_) {
		case tokenObjectBegin:
			successful = readObject(token);
			currentValue().setOffsetLimit(current_ - begin_);
			break;
		case tokenArrayBegin:
			successful = readArray(token);
			currentValue().setOffsetLimit(current_ - begin_);
			break;
		case tokenNumber:
			successful = decodeNumber(token);
			break;
		case tokenString:
			successful = decodeString(token);
			break;
		case tokenTrue: {
			Value v(true);
			currentValue().swapPayload(v);
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
		} break;
		case tokenFalse: {
			Value v(false);
			currentValue().swapPayload(v);
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
		} break;
		case tokenNull: {
			Value v;
			currentValue().swapPayload(v);
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
		} break;
		case tokenNaN: {
			Value v(std::numeric_limits<double>::quiet_NaN());
			currentValue().swapPayload(v);
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
		} break;
		case tokenPosInf: {
			Value v(std::numeric_limits<double>::infinity());
			currentValue().swapPayload(v);
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
		} break;
		case tokenNegInf: {
			Value v(-std::numeric_limits<double>::infinity());
			currentValue().swapPayload(v);
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
		} break;
		case tokenArraySeparator:
		case tokenObjectEnd:
		case tokenArrayEnd:
			if (features_.allowDroppedNullPlaceholders_) {
				// "Un-read" the current token and mark the current value as a null
				// token.
				current_--;
				Value v;
				currentValue().swapPayload(v);
				currentValue().setOffsetStart(current_ - begin_ - 1);
				currentValue().setOffsetLimit(current_ - begin_);
				break;
			} // else, fall through ...
		default:
			currentValue().setOffsetStart(token.start_ - begin_);
			currentValue().setOffsetLimit(token.end_ - begin_);
			return addError("Syntax error: value, object or array expected.", token);
		}

		if (collectComments_) {
			lastValueEnd_ = current_;
			lastValueHasAComment_ = false;
			lastValue_ = &currentValue();
		}

		return successful;
	}

	void OurReader::skipCommentTokens(Token& token) {
		if (features_.allowComments_) {
			do {
				readToken(token);
			} while (token.type_ == tokenComment);
		}
		else {
			readToken(token);
		}
	}

	bool OurReader::readToken(Token& token) {
		skipSpaces();
		token.start_ = current_;
		Char c = getNextChar();
		bool ok = true;
		switch (c) {
		case '{':
			token.type_ = tokenObjectBegin;
			break;
		case '}':
			token.type_ = tokenObjectEnd;
			break;
		case '[':
			token.type_ = tokenArrayBegin;
			break;
		case ']':
			token.type_ = tokenArrayEnd;
			break;
		case '"':
			token.type_ = tokenString;
			ok = readString();
			break;
		case '\'':
			if (features_.allowSingleQuotes_) {
				token.type_ = tokenString;
				ok = readStringSingleQuote();
			}
			else {
				// If we don't allow single quotes, this is a failure case.
				ok = false;
			}
			break;
		case '/':
			token.type_ = tokenComment;
			ok = readComment();
			break;
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			token.type_ = tokenNumber;
			readNumber(false);
			break;
		case '-':
			if (readNumber(true)) {
				token.type_ = tokenNumber;
			}
			else {
				token.type_ = tokenNegInf;
				ok = features_.allowSpecialFloats_ && match("nfinity", 7);
			}
			break;
		case '+':
			if (readNumber(true)) {
				token.type_ = tokenNumber;
			}
			else {
				token.type_ = tokenPosInf;
				ok = features_.allowSpecialFloats_ && match("nfinity", 7);
			}
			break;
		case 't':
			token.type_ = tokenTrue;
			ok = match("rue", 3);
			break;
		case 'f':
			token.type_ = tokenFalse;
			ok = match("alse", 4);
			break;
		case 'n':
			token.type_ = tokenNull;
			ok = match("ull", 3);
			break;
		case 'N':
			if (features_.allowSpecialFloats_) {
				token.type_ = tokenNaN;
				ok = match("aN", 2);
			}
			else {
				ok = false;
			}
			break;
		case 'I':
			if (features_.allowSpecialFloats_) {
				token.type_ = tokenPosInf;
				ok = match("nfinity", 7);
			}
			else {
				ok = false;
			}
			break;
		case ',':
			token.type_ = tokenArraySeparator;
			break;
		case ':':
			token.type_ = tokenMemberSeparator;
			break;
		case 0:
			token.type_ = tokenEndOfStream;
			break;
		default:
			ok = false;
			break;
		}
		if (!ok)
			token.type_ = tokenError;
		token.end_ = current_;
		return ok;
	}

	void OurReader::skipSpaces() {
		while (current_ != end_) {
			Char c = *current_;
			if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
				++current_;
			else
				break;
		}
	}

	void OurReader::skipBom(bool skipBom) {
		// The default behavior is to skip BOM.
		if (skipBom) {
			if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
				begin_ += 3;
				current_ = begin_;
			}
		}
	}

	bool OurReader::match(const Char* pattern, int patternLength) {
		if (end_ - current_ < patternLength)
			return false;
		int index = patternLength;
		while (index--)
			if (current_[index] != pattern[index])
				return false;
		current_ += patternLength;
		return true;
	}

	bool OurReader::readComment() {
		const Location commentBegin = current_ - 1;
		const Char c = getNextChar();
		bool successful = false;
		bool cStyleWithEmbeddedNewline = false;

		const bool isCStyleComment = (c == '*');
		const bool isCppStyleComment = (c == '/');
		if (isCStyleComment) {
			successful = readCStyleComment(&cStyleWithEmbeddedNewline);
		}
		else if (isCppStyleComment) {
			successful = readCppStyleComment();
		}

		if (!successful)
			return false;

		if (collectComments_) {
			CommentPlacement placement = commentBefore;

			if (!lastValueHasAComment_) {
				if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
					if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
						placement = commentAfterOnSameLine;
						lastValueHasAComment_ = true;
					}
				}
			}

			addComment(commentBegin, current_, placement);
		}
		return true;
	}

	String OurReader::normalizeEOL(OurReader::Location begin,
		OurReader::Location end) {
		String normalized;
		normalized.reserve(static_cast<size_t>(end - begin));
		OurReader::Location current = begin;
		while (current != end) {
			char c = *current++;
			if (c == '\r') {
				if (current != end && *current == '\n')
					// convert dos EOL
					++current;
				// convert Mac EOL
				normalized += '\n';
			}
			else {
				normalized += c;
			}
		}
		return normalized;
	}

	void OurReader::addComment(Location begin, Location end,
		CommentPlacement placement) {
		assert(collectComments_);
		const String& normalized = normalizeEOL(begin, end);
		if (placement == commentAfterOnSameLine) {
			assert(lastValue_ != nullptr);
			lastValue_->setComment(normalized, placement);
		}
		else {
			commentsBefore_ += normalized;
		}
	}

	bool OurReader::readCStyleComment(bool* containsNewLineResult) {
		*containsNewLineResult = false;

		while ((current_ + 1) < end_) {
			Char c = getNextChar();
			if (c == '*' && *current_ == '/')
				break;
			if (c == '\n')
				*containsNewLineResult = true;
		}

		return getNextChar() == '/';
	}

	bool OurReader::readCppStyleComment() {
		while (current_ != end_) {
			Char c = getNextChar();
			if (c == '\n')
				break;
			if (c == '\r') {
				// Consume DOS EOL. It will be normalized in addComment.
				if (current_ != end_ && *current_ == '\n')
					getNextChar();
				// Break on Moc OS 9 EOL.
				break;
			}
		}
		return true;
	}

	bool OurReader::readNumber(bool checkInf) {
		Location p = current_;
		if (checkInf && p != end_ && *p == 'I') {
			current_ = ++p;
			return false;
		}
		char c = '0'; // stopgap for already consumed character
		// integral part
		while (c >= '0' && c <= '9')
			c = (current_ = p) < end_ ? *p++ : '\0';
		// fractional part
		if (c == '.') {
			c = (current_ = p) < end_ ? *p++ : '\0';
			while (c >= '0' && c <= '9')
				c = (current_ = p) < end_ ? *p++ : '\0';
		}
		// exponential part
		if (c == 'e' || c == 'E') {
			c = (current_ = p) < end_ ? *p++ : '\0';
			if (c == '+' || c == '-')
				c = (current_ = p) < end_ ? *p++ : '\0';
			while (c >= '0' && c <= '9')
				c = (current_ = p) < end_ ? *p++ : '\0';
		}
		return true;
	}
	bool OurReader::readString() {
		Char c = 0;
		while (current_ != end_) {
			c = getNextChar();
			if (c == '\\')
				getNextChar();
			else if (c == '"')
				break;
		}
		return c == '"';
	}

	bool OurReader::readStringSingleQuote() {
		Char c = 0;
		while (current_ != end_) {
			c = getNextChar();
			if (c == '\\')
				getNextChar();
			else if (c == '\'')
				break;
		}
		return c == '\'';
	}

	bool OurReader::readObject(Token& token) {
		Token tokenName;
		String name;
		Value init(objectValue);
		currentValue().swapPayload(init);
		currentValue().setOffsetStart(token.start_ - begin_);
		while (readToken(tokenName)) {
			bool initialTokenOk = true;
			while (tokenName.type_ == tokenComment && initialTokenOk)
				initialTokenOk = readToken(tokenName);
			if (!initialTokenOk)
				break;
			if (tokenName.type_ == tokenObjectEnd &&
				(name.empty() ||
					features_.allowTrailingCommas_)) // empty object or trailing comma
				return true;
			name.clear();
			if (tokenName.type_ == tokenString) {
				if (!decodeString(tokenName, name))
					return recoverFromError(tokenObjectEnd);
			}
			else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
				Value numberName;
				if (!decodeNumber(tokenName, numberName))
					return recoverFromError(tokenObjectEnd);
				name = numberName.asString();
			}
			else {
				break;
			}
			if (name.length() >= (1U << 30))
				throwRuntimeError("keylength >= 2^30");
			if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
				String msg = "Duplicate key: '" + name + "'";
				return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
			}

			Token colon;
			if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
				return addErrorAndRecover("Missing ':' after object member name", colon,
					tokenObjectEnd);
			}
			Value& value = currentValue()[name];
			nodes_.push(&value);
			bool ok = readValue();
			nodes_.pop();
			if (!ok) // error already set
				return recoverFromError(tokenObjectEnd);

			Token comma;
			if (!readToken(comma) ||
				(comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
					comma.type_ != tokenComment)) {
				return addErrorAndRecover("Missing ',' or '}' in object declaration",
					comma, tokenObjectEnd);
			}
			bool finalizeTokenOk = true;
			while (comma.type_ == tokenComment && finalizeTokenOk)
				finalizeTokenOk = readToken(comma);
			if (comma.type_ == tokenObjectEnd)
				return true;
		}
		return addErrorAndRecover("Missing '}' or object member name", tokenName,
			tokenObjectEnd);
	}

	bool OurReader::readArray(Token& token) {
		Value init(arrayValue);
		currentValue().swapPayload(init);
		currentValue().setOffsetStart(token.start_ - begin_);
		int index = 0;
		for (;;) {
			skipSpaces();
			if (current_ != end_ && *current_ == ']' &&
				(index == 0 ||
					(features_.allowTrailingCommas_ &&
						!features_.allowDroppedNullPlaceholders_))) // empty array or trailing
				// comma
			{
				Token endArray;
				readToken(endArray);
				return true;
			}
			Value& value = currentValue()[index++];
			nodes_.push(&value);
			bool ok = readValue();
			nodes_.pop();
			if (!ok) // error already set
				return recoverFromError(tokenArrayEnd);

			Token currentToken;
			// Accept Comment after last item in the array.
			ok = readToken(currentToken);
			while (currentToken.type_ == tokenComment && ok) {
				ok = readToken(currentToken);
			}
			bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
				currentToken.type_ != tokenArrayEnd);
			if (!ok || badTokenType) {
				return addErrorAndRecover("Missing ',' or ']' in array declaration",
					currentToken, tokenArrayEnd);
			}
			if (currentToken.type_ == tokenArrayEnd)
				break;
		}
		return true;
	}

	bool OurReader::decodeNumber(Token& token) {
		Value decoded;
		if (!decodeNumber(token, decoded))
			return false;
		currentValue().swapPayload(decoded);
		currentValue().setOffsetStart(token.start_ - begin_);
		currentValue().setOffsetLimit(token.end_ - begin_);
		return true;
	}

	bool OurReader::decodeNumber(Token& token, Value& decoded) {
		// Attempts to parse the number as an integer. If the number is
		// larger than the maximum supported value of an integer then
		// we decode the number as a double.
		Location current = token.start_;
		const bool isNegative = *current == '-';
		if (isNegative) {
			++current;
		}

		// We assume we can represent the largest and smallest integer types as
		// unsigned integers with separate sign. This is only true if they can fit
		// into an unsigned integer.
		static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
			"Int must be smaller than UInt");

		// We need to convert minLargestInt into a positive number. The easiest way
		// to do this conversion is to assume our "threshold" value of minLargestInt
		// divided by 10 can fit in maxLargestInt when absolute valued. This should
		// be a safe assumption.
		static_assert(Value::minLargestInt <= -Value::maxLargestInt,
			"The absolute value of minLargestInt must be greater than or "
			"equal to maxLargestInt");
		static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
			"The absolute value of minLargestInt must be only 1 magnitude "
			"larger than maxLargest Int");

		static constexpr Value::LargestUInt positive_threshold =
			Value::maxLargestUInt / 10;
		static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;

		// For the negative values, we have to be more careful. Since typically
		// -Value::minLargestInt will cause an overflow, we first divide by 10 and
		// then take the inverse. This assumes that minLargestInt is only a single
		// power of 10 different in magnitude, which we check above. For the last
		// digit, we take the modulus before negating for the same reason.
		static constexpr auto negative_threshold =
			Value::LargestUInt(-(Value::minLargestInt / 10));
		static constexpr auto negative_last_digit =
			Value::UInt(-(Value::minLargestInt % 10));

		const Value::LargestUInt threshold =
			isNegative ? negative_threshold : positive_threshold;
		const Value::UInt max_last_digit =
			isNegative ? negative_last_digit : positive_last_digit;

		Value::LargestUInt value = 0;
		while (current < token.end_) {
			Char c = *current++;
			if (c < '0' || c > '9')
				return decodeDouble(token, decoded);

			const auto digit(static_cast<Value::UInt>(c - '0'));
			if (value >= threshold) {
				// We've hit or exceeded the max value divided by 10 (rounded down). If
				// a) we've only just touched the limit, meaing value == threshold,
				// b) this is the last digit, or
				// c) it's small enough to fit in that rounding delta, we're okay.
				// Otherwise treat this number as a double to avoid overflow.
				if (value > threshold || current != token.end_ ||
					digit > max_last_digit) {
					return decodeDouble(token, decoded);
				}
			}
			value = value * 10 + digit;
		}

		if (isNegative) {
			// We use the same magnitude assumption here, just in case.
			const auto last_digit = static_cast<Value::UInt>(value % 10);
			decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
		}
		else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
			decoded = Value::LargestInt(value);
		}
		else {
			decoded = value;
		}

		return true;
	}

	bool OurReader::decodeDouble(Token& token) {
		Value decoded;
		if (!decodeDouble(token, decoded))
			return false;
		currentValue().swapPayload(decoded);
		currentValue().setOffsetStart(token.start_ - begin_);
		currentValue().setOffsetLimit(token.end_ - begin_);
		return true;
	}

	bool OurReader::decodeDouble(Token& token, Value& decoded) {
		double value = 0;
		const String buffer(token.start_, token.end_);
		IStringStream is(buffer);
		if (!(is >> value)) {
			return addError(
				"'" + String(token.start_, token.end_) + "' is not a number.", token);
		}
		decoded = value;
		return true;
	}

	bool OurReader::decodeString(Token& token) {
		String decoded_string;
		if (!decodeString(token, decoded_string))
			return false;
		Value decoded(decoded_string);
		currentValue().swapPayload(decoded);
		currentValue().setOffsetStart(token.start_ - begin_);
		currentValue().setOffsetLimit(token.end_ - begin_);
		return true;
	}

	bool OurReader::decodeString(Token& token, String& decoded) {
		decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
		Location current = token.start_ + 1; // skip '"'
		Location end = token.end_ - 1;       // do not include '"'
		while (current != end) {
			Char c = *current++;
			if (c == '"')
				break;
			if (c == '\\') {
				if (current == end)
					return addError("Empty escape sequence in string", token, current);
				Char escape = *current++;
				switch (escape) {
				case '"':
					decoded += '"';
					break;
				case '/':
					decoded += '/';
					break;
				case '\\':
					decoded += '\\';
					break;
				case 'b':
					decoded += '\b';
					break;
				case 'f':
					decoded += '\f';
					break;
				case 'n':
					decoded += '\n';
					break;
				case 'r':
					decoded += '\r';
					break;
				case 't':
					decoded += '\t';
					break;
				case 'u': {
					unsigned int unicode;
					if (!decodeUnicodeCodePoint(token, current, end, unicode))
						return false;
					decoded += codePointToUTF8(unicode);
				} break;
				default:
					return addError("Bad escape sequence in string", token, current);
				}
			}
			else {
				decoded += c;
			}
		}
		return true;
	}

	bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
		Location end, unsigned int& unicode) {
		if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
			return false;
		if (unicode >= 0xD800 && unicode <= 0xDBFF) {
			// surrogate pairs
			if (end - current < 6)
				return addError(
					"additional six characters expected to parse unicode surrogate pair.",
					token, current);
			if (*(current++) == '\\' && *(current++) == 'u') {
				unsigned int surrogatePair;
				if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
					unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
				}
				else
					return false;
			}
			else
				return addError("expecting another \\u token to begin the second half of "
					"a unicode surrogate pair",
					token, current);
		}
		return true;
	}

	bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
		Location end,
		unsigned int& ret_unicode) {
		if (end - current < 4)
			return addError(
				"Bad unicode escape sequence in string: four digits expected.", token,
				current);
		int unicode = 0;
		for (int index = 0; index < 4; ++index) {
			Char c = *current++;
			unicode *= 16;
			if (c >= '0' && c <= '9')
				unicode += c - '0';
			else if (c >= 'a' && c <= 'f')
				unicode += c - 'a' + 10;
			else if (c >= 'A' && c <= 'F')
				unicode += c - 'A' + 10;
			else
				return addError(
					"Bad unicode escape sequence in string: hexadecimal digit expected.",
					token, current);
		}
		ret_unicode = static_cast<unsigned int>(unicode);
		return true;
	}

	bool OurReader::addError(const String& message, Token& token, Location extra) {
		ErrorInfo info;
		info.token_ = token;
		info.message_ = message;
		info.extra_ = extra;
		errors_.push_back(info);
		return false;
	}

	bool OurReader::recoverFromError(TokenType skipUntilToken) {
		size_t errorCount = errors_.size();
		Token skip;
		for (;;) {
			if (!readToken(skip))
				errors_.resize(errorCount); // discard errors caused by recovery
			if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
				break;
		}
		errors_.resize(errorCount);
		return false;
	}

	bool OurReader::addErrorAndRecover(const String& message, Token& token,
		TokenType skipUntilToken) {
		addError(message, token);
		return recoverFromError(skipUntilToken);
	}

	Value& OurReader::currentValue() { return *(nodes_.top()); }

	OurReader::Char OurReader::getNextChar() {
		if (current_ == end_)
			return 0;
		return *current_++;
	}

	void OurReader::getLocationLineAndColumn(Location location, int& line,
		int& column) const {
		Location current = begin_;
		Location lastLineStart = current;
		line = 0;
		while (current < location && current != end_) {
			Char c = *current++;
			if (c == '\r') {
				if (*current == '\n')
					++current;
				lastLineStart = current;
				++line;
			}
			else if (c == '\n') {
				lastLineStart = current;
				++line;
			}
		}
		// column & line start at 1
		column = int(location - lastLineStart) + 1;
		++line;
	}

	String OurReader::getLocationLineAndColumn(Location location) const {
		int line, column;
		getLocationLineAndColumn(location, line, column);
		char buffer[18 + 16 + 16 + 1];
		jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
		return buffer;
	}

	String OurReader::getFormattedErrorMessages() const {
		String formattedMessage;
		for (const auto& error : errors_) {
			formattedMessage +=
				"* " + getLocationLineAndColumn(error.token_.start_) + "\n";
			formattedMessage += "  " + error.message_ + "\n";
			if (error.extra_)
				formattedMessage +=
				"See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
		}
		return formattedMessage;
	}

	std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
		std::vector<OurReader::StructuredError> allErrors;
		for (const auto& error : errors_) {
			OurReader::StructuredError structured;
			structured.offset_start = error.token_.start_ - begin_;
			structured.offset_limit = error.token_.end_ - begin_;
			structured.message = error.message_;
			allErrors.push_back(structured);
		}
		return allErrors;
	}

	class OurCharReader : public CharReader {
		bool const collectComments_;
		OurReader reader_;

	public:
		OurCharReader(bool collectComments, OurFeatures const& features)
			: collectComments_(collectComments), reader_(features) {}
		bool parse(char const* beginDoc, char const* endDoc, Value* root,
			String* errs) override {
			bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
			if (errs) {
				*errs = reader_.getFormattedErrorMessages();
			}
			return ok;
		}
	};

	CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
	CharReaderBuilder::~CharReaderBuilder() = default;
	CharReader* CharReaderBuilder::newCharReader() const {
		bool collectComments = settings_["collectComments"].asBool();
		OurFeatures features = OurFeatures::all();
		features.allowComments_ = settings_["allowComments"].asBool();
		features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
		features.strictRoot_ = settings_["strictRoot"].asBool();
		features.allowDroppedNullPlaceholders_ =
			settings_["allowDroppedNullPlaceholders"].asBool();
		features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
		features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();

		// Stack limit is always a size_t, so we get this as an unsigned int
		// regardless of it we have 64-bit integer support enabled.
		features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
		features.failIfExtra_ = settings_["failIfExtra"].asBool();
		features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
		features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
		features.skipBom_ = settings_["skipBom"].asBool();
		return new OurCharReader(collectComments, features);
	}

	bool CharReaderBuilder::validate(Json::Value* invalid) const {
		static const auto& valid_keys = *new std::set<String>{
			"collectComments",
			"allowComments",
			"allowTrailingCommas",
			"strictRoot",
			"allowDroppedNullPlaceholders",
			"allowNumericKeys",
			"allowSingleQuotes",
			"stackLimit",
			"failIfExtra",
			"rejectDupKeys",
			"allowSpecialFloats",
			"skipBom",
		};
		for (auto si = settings_.begin(); si != settings_.end(); ++si) {
			auto key = si.name();
			if (valid_keys.count(key))
				continue;
			if (invalid)
				(*invalid)[key] = *si;
			else
				return false;
		}
		return invalid ? invalid->empty() : true;
	}

	Value& CharReaderBuilder::operator[](const String& key) {
		return settings_[key];
	}
	// static
	void CharReaderBuilder::strictMode(Json::Value* settings) {
		//! [CharReaderBuilderStrictMode]
		(*settings)["allowComments"] = false;
		(*settings)["allowTrailingCommas"] = false;
		(*settings)["strictRoot"] = true;
		(*settings)["allowDroppedNullPlaceholders"] = false;
		(*settings)["allowNumericKeys"] = false;
		(*settings)["allowSingleQuotes"] = false;
		(*settings)["stackLimit"] = 1000;
		(*settings)["failIfExtra"] = true;
		(*settings)["rejectDupKeys"] = true;
		(*settings)["allowSpecialFloats"] = false;
		(*settings)["skipBom"] = true;
		//! [CharReaderBuilderStrictMode]
	}
	// static
	void CharReaderBuilder::setDefaults(Json::Value* settings) {
		//! [CharReaderBuilderDefaults]
		(*settings)["collectComments"] = true;
		(*settings)["allowComments"] = true;
		(*settings)["allowTrailingCommas"] = true;
		(*settings)["strictRoot"] = false;
		(*settings)["allowDroppedNullPlaceholders"] = false;
		(*settings)["allowNumericKeys"] = false;
		(*settings)["allowSingleQuotes"] = false;
		(*settings)["stackLimit"] = 1000;
		(*settings)["failIfExtra"] = false;
		(*settings)["rejectDupKeys"] = false;
		(*settings)["allowSpecialFloats"] = false;
		(*settings)["skipBom"] = true;
		//! [CharReaderBuilderDefaults]
	}

	//////////////////////////////////
	// global functions

	bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
		String* errs) {
		OStringStream ssin;
		ssin << sin.rdbuf();
		String doc = ssin.str();
		char const* begin = doc.data();
		char const* end = begin + doc.size();
		// Note that we do not actually need a null-terminator.
		CharReaderPtr const reader(fact.newCharReader());
		return reader->parse(begin, end, root, errs);
	}

	IStream& operator>>(IStream& sin, Value& root) {
		CharReaderBuilder b;
		String errs;
		bool ok = parseFromStream(b, sin, &root, &errs);
		if (!ok) {
			throwRuntimeError(errs);
		}
		return sin;
	}
} // namespace Json